package cmd

import (
	"encoding/json"
	"lark/admin_server"
	"lark/client_agent"
	"lark/conf"
	"lark/server_agent"
	"lark/util"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"time"

	"lark/dove"

	"github.com/spf13/cobra"
	"github.com/zeast/logs"
)

var StartCmd = &cobra.Command{
	Use:   "start",
	Short: "start lark",
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		logs.LogFuncCall(true)

		if daemon {
			fork()
		}

		var err error
		err = util.EnsureOpenFiles(conf.OSMaxOpenFiles)
		if err != nil {
			logs.Fatalf("%+v", err)
		}

		//方便开发环境设置路径
		if !cmd.PersistentFlags().Lookup("client_agent_log_filename").Changed {
			if x := os.Getenv("LARK_CLIENT_AGENT_LOG_FILENAME"); x != "" {
				conf.ClientAgentLogFileName = x
			}
		}

		if !cmd.PersistentFlags().Lookup("client_agent_log_level").Changed {
			if x := os.Getenv("LARK_CLIENT_AGENT_LOG_LEVEL"); x != "" {
				conf.ClientAgentLogLevel = x
			}
		}

		if !cmd.PersistentFlags().Lookup("server_agent_log_filename").Changed {
			if x := os.Getenv("LARK_SERVER_AGENT_LOG_FILENAME"); x != "" {
				conf.ServerAgentLogFileName = x
			}
		}

		if !cmd.PersistentFlags().Lookup("server_agent_log_level").Changed {
			if x := os.Getenv("LARK_SERVER_AGENT_LOG_LEVEL"); x != "" {
				conf.ServerAgentLogLevel = x
			}
		}

		//etcd 集群地址的参数检查
		if len(conf.EtcdEndpoints) == 0 {
			data, err := dove.Get(conf.DoveKeyOfEtcdEndpoints)
			if err != nil {
				logs.Fatal(err)
			}

			err = json.Unmarshal(data, &conf.EtcdEndpoints)
			if err != nil {
				logs.Fatal(err)
			}
		}

		if len(conf.AdminWhitelist) == 0 {
			data, err := dove.Get(conf.DoveKeyOfAdminWhitelist)
			if err != nil {
				logs.Fatal(err)
			}

			err = json.Unmarshal(data, &conf.AdminWhitelist)
			if err != nil {
				logs.Fatal(err)
			}
		}

		if len(conf.BackupEtcdEndpoints) == 0 {
			data, err := dove.Get(conf.DoveKeyOfBackupEtcdEndpoints)
			if err != nil {
				logs.Fatal(err)
			}

			err = json.Unmarshal(data, &conf.BackupEtcdEndpoints)
			if err != nil {
				logs.Fatal(err)
			}
		}
	},

	Run: func(cmd *cobra.Command, args []string) {
		logs.Debugf("lark start. Version:%s, GitHash:%s, BuildTime:%s", conf.Version, conf.GitHash, conf.BuildTime)

		admin_server.StartHTTP()

		if err := client_agent.Init(); err != nil {
			logs.Fatalf("%+v", err)
		}

		if err := server_agent.Init(); err != nil {
			logs.Fatalf("%+v", err)
		}

		sc := make(chan os.Signal, 1)
		signal.Notify(sc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
		for sig := range sc {
			logs.Debugf("receive signal:%s, exit", sig.String())
			break
		}
	},
}

func fork() {
	var cmd *exec.Cmd
	var wantExit bool
	var startTime = time.Now()

	go func() {
		sc := make(chan os.Signal, 1)
		signal.Notify(sc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
		for sig := range sc {
			logs.Debugf("receive signal:%s, exit", sig.String())
			wantExit = true
			if cmd != nil && cmd.Process != nil {
				if err := cmd.Process.Kill(); err != nil {
					logs.Errorf("stop child progamme error:%s", err)
				} else {
					logs.Debugf("stop child progamme success")
				}
			}
			return
		}
	}()

	os.Args = append(os.Args, "--daemon=false")

	for {
		cmd = exec.Command(os.Args[0], os.Args[1:]...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			logs.Errorf("child programme exit with error:%s", err)
		}
		time.Sleep(time.Millisecond * 100)

		if wantExit {
			break
		}

		if time.Since(startTime) < time.Second*2 {
			break
		}
	}

	logs.Debugf("parent programme exit")
	os.Exit(0)
}
